Next: Nested Formulas with Rewrite Rules, Previous: Other Features of Rewrite Rules, Up: Rewrite Rules
There are three operators, ‘&&&’, ‘|||’, and ‘!!!’, that combine rewrite patterns to make larger patterns. The combinations are “and,” “or,” and “not,” respectively, and these operators are the pattern equivalents of ‘&&’, ‘||’ and ‘!’ (which operate on zero-or-nonzero logical values).
Note that ‘&&&’, ‘|||’, and ‘!!!’ are left in symbolic form by all regular Calc features; they have special meaning only in the context of rewrite rule patterns.
The pattern ‘p1 &&& p2’ matches anything that matches both p1 and p2. One especially useful case is when one of p1 or p2 is a meta-variable. For example, here is a rule that operates on error forms:
f(x &&& a +/- b, x) := g(x)
This does the same thing, but is arguably simpler than, the rule
f(a +/- b, a +/- b) := g(a +/- b)
Here's another interesting example:
ends(cons(a, x) &&& rcons(y, b)) := [a, b]
which effectively clips out the middle of a vector leaving just the first and last elements. This rule will change a one-element vector ‘[a]’ to ‘[a, a]’. The similar rule
ends(cons(a, rcons(y, b))) := [a, b]
would do the same thing except that it would fail to match a one-element vector.
The pattern ‘p1 ||| p2’ matches anything that matches either p1 or p2. Calc first tries matching against p1; if that fails, it goes on to try p2.
curve(inf ||| -inf) := 0
which converts both ‘curve(inf)’ and ‘curve(-inf)’ to zero.
Here is a larger example:
log(a, b) ||| (ln(a) :: let(b := e)) := mylog(a, b)
This matches both generalized and natural logarithms in a single rule. Note that the ‘::’ term must be enclosed in parentheses because that operator has lower precedence than ‘|||’ or ‘:=’.
(In practice this rule would probably include a third
alternative, omitted here for brevity, to take care of
log10.)
While Calc generally treats interior conditions exactly the
same as conditions on the outside of a rule, it does guarantee
that if all the variables in the condition are special names like
e, or already bound in the pattern to which the
condition is attached (say, if ‘a’ had appeared in this condition),
then Calc will process this condition right after matching the
pattern to the left of the ‘::’. Thus, we know that
‘b’ will be
bound to ‘e’
only if the ln branch of the
‘|||’ was
taken.
Note that this rule was careful to bind the same set of meta-variables on both sides of the ‘|||’. Calc does not check this, but if you bind a certain meta-variable only in one branch and then use that meta-variable elsewhere in the rule, results are unpredictable:
f(a,b) ||| g(b) := h(a,b)
Here if the pattern matches ‘g(17)’, Calc makes no promises about the value that will be substituted for ‘a’ on the righthand side.
The pattern ‘!!! pat’ matches anything that does not match pat. Any meta-variables that are bound while matching pat remain unbound outside of pat.
For example,
f(x &&& !!! a +/- b, !!![]) := g(x)
converts f whose first argument
is anything except an error form, and whose second
argument is not the empty vector, into a similar call to
g (but without the second argument).
If we know that the second argument will be a vector (empty or not), then an equivalent rule would be:
f(x, y) := g(x) :: typeof(x) != 7 :: vlen(y) > 0
where of course 7 is the typeof
code for error forms. Another final condition, that works for any
kind of ‘y’,
would be ‘!istrue(y ==
[])’. (The istrue function
returns an explicit 0 if its argument was left in symbolic form;
plain ‘!(y ==
[])’ or ‘y !=
[]’ would not work to replace
‘!!![]’ since
these would be left unsimplified, and thus cause the rule to
fail, if ‘y’
was something like a variable name.)
It is possible for a ‘!!!’ to refer to meta-variables bound elsewhere in the pattern. For example,
f(a, !!!a) := g(a)
matches any call to f with
different arguments, changing this to g with only
the first argument.
If a function call is to be matched and one of the argument patterns contains a ‘!!!’ somewhere inside it, that argument will be matched last. Thus
f(!!!a, a) := g(a)
will be careful to bind
‘a’ to the
second argument of f before testing the first
argument. If Calc had tried to match the first argument of
f first, the results would have been disastrous:
since a was unbound so far, the pattern
‘a’ would have
matched anything at all, and the pattern
‘!!!a’
therefore would not have matched anything at all!